<?php

namespace App\Jobs;

use App\Models\Agent;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class AdjustAgentAndSubParentPathJob extends Job
{
    private $agentId;

    /**
     * 被移动的用户实例
     * @var
     */
    private $movedAgent;

    private $originDirectInviter;

    private $originIndirectInviter;

    /**
     * 代理层级关系树
     * @var array
     */
    private $relationshipTree = [];

    /**
     * 有合伙人身份信息的代理
     * 代理ID => 合伙人信息
     * @var array
     */
    private $hasPartnerIdentityAgent = [];

    public function __construct($agentId, $originDirectInviter, $originIndirectInviter)
    {
        $this->agentId = $agentId;
        $this->originDirectInviter = $originDirectInviter;
        $this->originIndirectInviter = $originIndirectInviter;
    }

    public function handle()
    {
        try {
            \DB::beginTransaction();

            [$parentPathId, $partner] = $this->getUpdateDate($this->agentId); // 当前用户需要更新的内容
            $this->movedAgent = Agent::query()
                ->select(['id', 'direct_inviter', 'indirect_inviter'])
                ->with(['directInviter:id', 'indirectInviter:id'])
                ->findOrFail($this->agentId);
            $this->movedAgent->update(['parent_path_id' => $parentPathId, 'affiliated_partner' => $partner]);

            // 用户的下级, 以及递归向下(parent_path_id 和 所属合伙人 需要更新)
            $this->updateChildren($this->agentId, $level = 1);

            // 直接更新 原来 邀请人的邀请人数
            if ($this->originDirectInviter) {
                // 原来的直接邀请人 更新邀请人数
                Agent::query()->findOrFail($this->originDirectInviter)->update([
                    'number_of_direct_inviter' => Agent::query()->where('direct_inviter', $this->originDirectInviter)->count(),
                    'number_of_indirect_inviter' => Agent::query()->where('indirect_inviter', $this->originDirectInviter)->count()
                ]);
            }
            if ($this->originIndirectInviter) {
                // 原来的间接邀请人 更新邀请人数
                Agent::query()->findOrFail($this->originIndirectInviter)->update([
                    'number_of_indirect_inviter' => Agent::query()->where('indirect_inviter', $this->originIndirectInviter)->count()
                ]);
            }
            // 直接更新 新的 邀请人的邀请人数
            if ($this->movedAgent->directInviter) {
                $this->movedAgent->directInviter->update([
                    'number_of_direct_inviter' => Agent::query()->where('direct_inviter', $this->movedAgent->directInviter['id'])->count(),
                    'number_of_indirect_inviter' => Agent::query()->where('indirect_inviter', $this->movedAgent->directInviter['id'])->count()
                ]);
            }
            if ($this->movedAgent->indirectInviter) {
                $this->movedAgent->indirectInviter->update([
                    'number_of_indirect_inviter' => Agent::query()->where('indirect_inviter', $this->movedAgent->indirectInviter['id'])->count()
                ]);
            }

            \DB::commit();
        } catch (\Exception $e) {
            \DB::rollBack();
            \Log::error(sprintf('处理用户变更上级 Job 发生错误: %s (%s [%d])', $e->getMessage(), $e->getFile(), $e->getLine()));
        }

        \Log::info('处理用户变更上级 Job 完成:' . $this->agentId);
    }

    /**
     * 递归更新下级
     * @param $agentId int 代理 ID
     * @param $level int 递归的层级
     */
    private function updateChildren($agentId, $level)
    {
        Agent::query()->select(['id'])->where('direct_inviter', $agentId)->each(function (Agent $agent) use ($level) {
            // 变更邀请人
            // 层级 1 => 直接邀请的对象, 更改他们的间接邀请
            if ($level === 1 && $this->movedAgent->directInviter) {
                $agent->update(['indirect_inviter' => $this->movedAgent->directInviter['id']]);
            }

            [$parentPathId, $partner] = $this->getUpdateDate($agent['id']);
            $agent->update(['parent_path_id' => $parentPathId, 'affiliated_partner' => $partner]);

            $this->updateChildren($agent['id'], $level + 1);
        });
    }

    /**
     * 获取更新的字段内容
     */
    private function getUpdateDate($agentId)
    {
        $this->superiorRoute($agentId); // 读取用户的现在(变更后)上级路线

        $agentSuperiorArr = [];
        $this->getSuperiorRouteArray($agentId, $agentSuperiorArr); // 获取用户的现在(变更后)上级路线
        $partner = $this->getNearestPartnerFromSuperiorRoute($agentSuperiorArr); // 从上级中获取最近的合伙人

        return [implode('-', $agentSuperiorArr), $partner];
    }

    /**
     * 查询上级路线
     * @param $agentId
     */
    private function superiorRoute($agentId)
    {
        if (isset($this->relationshipTree[$agentId])) {
            return;
        }

        // 这里一级级递归, 这样适合做 "缓存"
        $agent = Agent::query()
            ->select(['id', 'direct_inviter'])
            ->with([
                'partnerIdentity', // 当前用户的合伙人身份
                'directInviter' => function (BelongsTo $belongsTo) {
                    $belongsTo->select(['id'])->with(['partnerIdentity']);
                } // 直接邀请人 和 直接邀请人合伙人身份
            ])
            ->findOrFail($agentId)
            ->toArray();

        // 合伙人身份
        $agent['partner_identity'] !== null && ($this->hasPartnerIdentityAgent[$agent['id']] = true); // 当前代理是否有合伙人身份

        // 层级关系
        if ($agent['direct_inviter'] !== null) {
            // 当前代理 =(的上级)=> 直接邀请人
            $this->relationshipTree[$agent['id']] = $agent['direct_inviter']['id'];

            // 直接邀请人是否有合伙人身份
            ($agent['direct_inviter']['partner_identity'] ?? null) !== null && ($this->hasPartnerIdentityAgent[$agent['direct_inviter']['id']] = true);
            // 如果有直接邀请人,则递归
            $this->superiorRoute($agent['direct_inviter']['id']);
        }
    }

    /**
     * 获取上级路线数组
     */
    private function getSuperiorRouteArray($agentId, &$superiorArr)
    {
        if (!isset($this->relationshipTree[$agentId])) {
            return;
        }

        array_unshift($superiorArr, $this->relationshipTree[$agentId]);
        $this->getSuperiorRouteArray($this->relationshipTree[$agentId], $superiorArr);
    }

    /**
     * 从上级中获取最近的合伙人
     * @param $agentSuperiorArr
     * @return mixed|null
     */
    private function getNearestPartnerFromSuperiorRoute($agentSuperiorArr)
    {
        $partner = null;
        foreach (array_reverse($agentSuperiorArr) as $agent) {
            if (isset($this->hasPartnerIdentityAgent[$agent])) {
                $partner = $agent;
                break;
            }
        }

        return $partner;
    }
}
